﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

namespace zijian666.AnyExtensions;

/// <summary>
/// 异常扩展
/// </summary>
public static class ExceptionExtensions
{
    /// <summary>
    /// 开启参数校验
    /// </summary>
    public static ParameterChecker<T> Check<T>(this T value, string paramName, [CallerMemberName] string? method = null)
        => new(value, paramName, method!);

    /// <summary>
    /// 如果参数值为null 则抛出异常
    /// </summary>
    /// <exception cref="ArgumentNullException"></exception>
    public static ParameterChecker<T> ThrowIfNull<T>(this ParameterChecker<T> checker, string? message = null)
    {
        checker.ThrowIfInvalid();
        if (checker.Value is null)
        {
            throw new ArgumentNullException(checker.Name, $"调用{checker.Method}参数异常：{message ?? (checker.Name + "不能为null")}");
        }
        return checker;
    }

    /// <summary>
    /// 如果参数值是默认值 则抛出异常
    /// </summary>
    /// <exception cref="ArgumentNullException"></exception>
    public static ParameterChecker<T> ThrowIfDefault<T>(this ParameterChecker<T> checker, string? message = null)
        where T : struct
    {
        checker.ThrowIfInvalid();
        if (checker.Value.Equals(default(T)))
        {
            throw new ArgumentNullException(checker.Name, $"调用{checker.Method}参数异常：{message ?? (checker.Name + "不能为" + default(T))}");
        }
        return checker;
    }

    /// <summary>
    /// 如果参数值是空集合，空字符串，或null 则抛出异常
    /// </summary>
    /// <exception cref="ArgumentNullException"></exception>
    public static ParameterChecker<T> ThrowIfEmpty<T>(this ParameterChecker<T> checker, string? message = null)
        where T : IEnumerable
    {
        checker.ThrowIfInvalid();
        if (checker.Value is null)
        {
            return checker;
        }
        if (checker.Value is ICollection collection)
        {
            if (collection.Count > 0)
            {
                return checker;
            }
        }
        else
        {
            foreach (var _ in checker.Value)
            {
                return checker;
            }
        }

        message ??= checker.Name + "不能为空" + (checker.Value is string ? "字符串" : "集合");

        throw new ArgumentNullException(checker.Name, $"调用{checker.Method}参数错误：{message}");
    }

    /// <summary>
    /// 如果存在一个以上的异常，则尝试聚合异常后抛出; 如果只有一个异常直接抛出
    /// </summary>
    public static void ThrowAllError<T>(this IEnumerable<T>? exceptions, [CallerMemberName] string? method = null, [CallerFilePath] string? file = null, [CallerLineNumber] int line = 0)
        where T : Exception
        => exceptions.Aggregate().Throw(method, file, line);

    /// <summary>
    /// 抛出第一个异常，如果没有则不执行任何操作
    /// </summary>
    public static void ThrowFirstError<T>(this IEnumerable<T>? exceptions, [CallerMemberName] string? method = null, [CallerFilePath] string? file = null, [CallerLineNumber] int line = 0)
        where T : Exception
        => exceptions.WithoutNull().FirstOrDefault().Throw(method, file, line);

    /// <summary>
    /// 如果异常不为空，则抛出异常
    /// </summary>
    public static void Throw<T>(this T? exception, [CallerMemberName] string? method = null, [CallerFilePath] string? file = null, [CallerLineNumber] int line = 0)
        where T : Exception
    {
        if (exception is not null)
        {
            exception.Data["Method"] = method;
            exception.Data["File"] = file;
            exception.Data["Line"] = line;
            throw exception;
        }
    }

    /// <summary>
    /// 如果有必要，聚合异常，默认最大聚合异常10个，超过则丢弃
    /// </summary>
    /// <param name="max">最大聚合数量, 超过该值则抛弃</param>
    public static Exception? Aggregate<T>(this IEnumerable<T>? exceptions, int max = 10)
        where T : Exception
    {
        if (exceptions is null || max <= 0)
        {
            return null;
        }
        var errors = exceptions.WithoutNull().Take(max).ToArray();

        if (errors.Length == 0)
        {
            return null;
        }
        if (errors.Length == 1)
        {
            return errors[0];
        }
        return new MyAggregateException(errors[0].Message + "还有更多异常(" + errors.Length + ")", errors);
    }

    /// <summary>
    /// 循环当前异常的所有内部异常
    /// </summary>
    public static IEnumerable<T> GetAllExceptions<T>(this Exception? exception)
        where T : Exception
    {
        if (exception is T t)
        {
            yield return t;
        }

        if (exception?.InnerException is null)
        {
            yield break;
        }

        var list = new List<Exception>() { exception };

        for (var i = 0; i < list.Count; i++)
        {
            var exception1 = list[i];

            if (exception1 is AggregateException agg)
            {
                foreach (var exception2 in agg.InnerExceptions)
                {
                    if (!list.Contains(exception2))
                    {
                        list.Add(exception2);
                        if (exception2 is T t1)
                        {
                            yield return t1;
                        }
                    }
                }
            }
            else if (exception1.InnerException is not null)
            {
                var exception2 = exception1.InnerException;
                if (!list.Contains(exception2))
                {
                    list.Add(exception2);
                    if (exception2 is T t1)
                    {
                        yield return t1;
                    }
                }
            }
        }
    }

    /// <summary>
    /// 循环当前异常的所有指定类型的内部异常
    /// </summary>
    public static IEnumerable<Exception> GetAllExceptions(this Exception? exception)
        => GetAllExceptions<Exception>(exception);

    #region private

    private class MyAggregateException(string message, IEnumerable<Exception> innerExceptions)
        : AggregateException(message, innerExceptions)
    {
        private readonly string _message = message;

        public override string Message => _message;
    }

    #endregion
}

/// <summary>
/// 参数校验
/// </summary>
/// <typeparam name="T">参数类型</typeparam>
/// <param name="Value">参数值</param>
/// <param name="Name">参数名称</param>
/// <param name="Method">方法名称</param>
public readonly ref struct ParameterChecker<T>(T Value, string Name, string Method)
{
    /// <summary>
    /// 参数名称
    /// </summary>
    public readonly string Name = Name;
    /// <summary>
    /// 方法名称
    /// </summary>
    public readonly string Method = Method;
    /// <summary>
    /// 参数值
    /// </summary>
    public readonly T Value = Value;

    internal void ThrowIfInvalid()
    {
        _ = Name ?? throw new ArgumentNullException(nameof(Name));
        _ = Method ?? throw new ArgumentNullException(nameof(Method));
    }
}
